﻿using System;
using System.IO;
using System.Reflection;
using System.Text;
using Useful.Sys;

namespace Useful.Document
{
    public abstract class BasePdfBatchPrinter : IBatchPrinter
    {
        protected const string PRINTERNAME = "Bullzip PDF Printer";

        protected Type PDFPrinterSettingsType = null;
        protected Type PDFPrinterUtilType = null;
        protected object com_appPrinterSettings = null;
        protected object com_appPrinterUtil = null;

        protected StringBuilder mergeFiles = null;
        protected readonly string gilyash = null;

        protected abstract void CreateComObj();

        protected BasePdfBatchPrinter(string gilyash)
        {
            this.gilyash = gilyash;
        }

        protected void Init()
        {
            try
            {
                CreateComObj();
            }
            catch (Exception ex)
            {
                throw new Exception(String.Format("Ошибка при инициализации PDF-принтера '{0}'.\r\nВозможно PDF-принтер не установлен. \r\n{1}", PRINTERNAME, ex.Message), ex);
            }
        }

        public void PrintBatch(string name, string[] files, bool encrypt, string ownerPassword)
        {
            mergeFiles = new StringBuilder();

            for (int i = 0; i < files.Length; i++)
            {
                string file = PrintFileCore(files[i]);

                if (i < files.Length - 1)
                    mergeFiles.Append(mergeFiles.Length == 0 ? file : "|" + file);
            }

            PrintFile(files[files.Length - 1], mergeFiles.ToString(), name, encrypt, ownerPassword);
        }

        private string PrintFileCore(string file)
        {
            string res = Path.ChangeExtension(file, "pdf");

            string outputFileName = SetSettings(res, null, false, null);

            PrintPdfFile(file, outputFileName);

            return outputFileName;
        }

        protected static string GetFileName(string fullFileName)
        {
            return fullFileName;
        }

        protected virtual string GetLocalFileName(string file, bool includeHostName)
        {
            return GetFileName(file);
        }

        private void PrintPdfFile(string file, string outputFileName)
        {
            bool exists;

            try
            {
                exists = Sys.SystemHelper.TestExistFile(GetLocalFileName(outputFileName, true), true);
            }
            catch (Exception ex)
            {
                throw new Exception("BasePdfBatchPrinter.PrintPdfFile (TestExistFile): " + ex.Message);
            }

            if (!exists)
            {
                string tmpFullFileName = GetLocalFileName(file, false);

                try
                {
                    PDFPrinterUtilType.InvokeMember("PrintFile", BindingFlags.InvokeMethod, null, com_appPrinterUtil, new object[] { tmpFullFileName, PRINTERNAME });
                }
                catch (Exception ex)
                {
                    throw new Exception("BasePdfBatchPrinter.PrintPdfFile (InvokeMember PrintFile): " + ex.Message);
                }

                try
                {
                    PDFPrinterUtilType.InvokeMember("WaitForFile", BindingFlags.InvokeMethod, null, com_appPrinterUtil, new object[] { outputFileName, 5000 });

                    SystemHelper.WaitFileReady(outputFileName, 3000, 3);
                }
                catch (Exception ex)
                {
                    throw new Exception("BasePdfBatchPrinter.PrintPdfFile (InvokeMember WaitForFile): " + ex.Message);
                }
            }
            else
                throw new Exception(String.Format("Не удаётся удалить старый PDF-файл (BasePdfBatchPrinter.PrintPdfFile({0}, {1}))", file, outputFileName));
        }

        public virtual void PrintFile(string file, string mergeFiles, string newName, bool protect, string ownerPassword)
        {
            string outputFileName = SetSettings(newName, mergeFiles, protect, ownerPassword);

            PrintPdfFile(file, outputFileName);
        }

        protected virtual string SetSettings(string name, string mergeFiles, bool protect, string ownerPassword)
        {
            string tmpFullFileName = GetLocalFileName(name, false);

            try
            {

                PDFPrinterSettingsType.InvokeMember("PrinterName", BindingFlags.SetProperty, null, com_appPrinterSettings, new object[] { PRINTERNAME });

                PDFPrinterSettingsType.InvokeMember("SetValue", BindingFlags.InvokeMethod, null, com_appPrinterSettings, new object[] { "Output", tmpFullFileName });
                PDFPrinterSettingsType.InvokeMember("SetValue", BindingFlags.InvokeMethod, null, com_appPrinterSettings, new object[] { "ShowSettings", "never" });
                PDFPrinterSettingsType.InvokeMember("SetValue", BindingFlags.InvokeMethod, null, com_appPrinterSettings, new object[] { "ConfirmOverwrite", "no" });
                PDFPrinterSettingsType.InvokeMember("SetValue", BindingFlags.InvokeMethod, null, com_appPrinterSettings, new object[] { "ShowPDF", "no" });

                if (!String.IsNullOrEmpty(mergeFiles))
                {
                    PDFPrinterSettingsType.InvokeMember("SetValue", BindingFlags.InvokeMethod, null, com_appPrinterSettings, new object[] { "MergeFile", mergeFiles });
                    PDFPrinterSettingsType.InvokeMember("SetValue", BindingFlags.InvokeMethod, null, com_appPrinterSettings, new object[] { "MergePosition", "bottom" });
                }

                if (!String.IsNullOrEmpty(gilyash))
                {
                    PDFPrinterSettingsType.InvokeMember("SetValue", BindingFlags.InvokeMethod, null, com_appPrinterSettings, new object[] { "Superimpose", gilyash });
                }

                if (protect)
                {
                    PDFPrinterSettingsType.InvokeMember("SetValue", BindingFlags.InvokeMethod, null, com_appPrinterSettings,
                                                    new object[] { "KeyLength", "128" });
                    PDFPrinterSettingsType.InvokeMember("SetValue", BindingFlags.InvokeMethod, null, com_appPrinterSettings,
                                                    new object[] { "OwnerPassword", ownerPassword });
                    PDFPrinterSettingsType.InvokeMember("SetValue", BindingFlags.InvokeMethod, null, com_appPrinterSettings,
                                                    new object[] { "UserPassword", "" });
                    PDFPrinterSettingsType.InvokeMember("SetValue", BindingFlags.InvokeMethod, null, com_appPrinterSettings,
                                                    new object[] { "Permissions", "61652" });
                }

                PDFPrinterSettingsType.InvokeMember("SetValue", BindingFlags.InvokeMethod, null, com_appPrinterSettings, new object[] { "ShowProgressFinished", "no" });
                PDFPrinterSettingsType.InvokeMember("SetValue", BindingFlags.InvokeMethod, null, com_appPrinterSettings, new object[] { "ShowSaveAs", "never" });
                PDFPrinterSettingsType.InvokeMember("SetValue", BindingFlags.InvokeMethod, null, com_appPrinterSettings, new object[] { "ShowProgress", "yes" });

                PDFPrinterSettingsType.InvokeMember("WriteSettings", BindingFlags.InvokeMethod, null, com_appPrinterSettings, new object[] { true });
            }
            catch (Exception ex)
            {
                throw new Exception("BasePdfBatchPrinter.SetSettings: " + ex.Message);
            }

            return tmpFullFileName;
        }

        public abstract void Dispose();
    }
}
